﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.IO;
using System.Runtime.InteropServices;
using Emgu.CV;
using Emgu.Util;
using Emgu.CV.Structure;
using Emgu.CV.CvEnum;

namespace KegPlugin
{
    // Uses EigenFaces to detect a person in an image taken from the webcam on the kegerator
    public class FaceCapture : IDisposable
    {
        private Capture capture;
        private HaarCascade haar;
        private Image<Bgr, byte> nextFrame;
        bool Initialized = false;

        Image<Gray, Byte>[] trainingImages;
        String[] trainingLabels;
        MCvTermCriteria termCrit;
        EigenObjectRecognizer Recognizer;

        bool trainingInitialized = false; 

        public void Dispose()
        {
            nextFrame.Dispose();
            capture.Dispose();
            haar.Dispose();
        }

        public bool IsInitialized()
        {
            return Initialized;
        }

        public void Initialize()
        {
            // Use the default camera
            capture = new Capture();
            haar = new HaarCascade(".\\haarcascade_frontalface_alt_tree.xml");

            // Sleep for a short period, in case the camera needs to warm up.
            // Otherwise it will simply be a black image.
            System.Threading.Thread.Sleep(500);
            Initialized = true;
        }

        public void Capture()
        {
            nextFrame = capture.QueryFrame();
        }

        public void Capture(String fileName)
        {
            Capture();
            Save(fileName);
        }

        public void Save(String fileName)
        {
            // Parse the extension from the filename
            String extension = Path.GetExtension(fileName);
            System.Drawing.Imaging.ImageFormat Format = System.Drawing.Imaging.ImageFormat.Bmp;

            if (String.Equals(extension, "jpg", StringComparison.CurrentCultureIgnoreCase)
                || String.Equals(extension, "jpeg", StringComparison.CurrentCultureIgnoreCase))
            {
                Format = System.Drawing.Imaging.ImageFormat.Jpeg;
            }
            else if (String.Equals(extension, "bmp", StringComparison.CurrentCultureIgnoreCase))
            {
                Format = System.Drawing.Imaging.ImageFormat.Png;
            }

            Bitmap bmp = nextFrame.ToBitmap();
            bmp.Save(fileName, System.Drawing.Imaging.ImageFormat.Bmp);
        }

        // TODO: Modify to only return the largest face found
        public MCvAvgComp[] DetectFaces(bool markFaces)
        {
            MCvAvgComp[] faces = null;

            if (nextFrame != null)
            {
                // there's only one channel (greyscale), hence the zero index
                //var faces = nextFrame.DetectHaarCascade(haar)[0];
                Image<Gray, byte> grayframe = nextFrame.Convert<Gray, byte>();
                faces = grayframe.DetectHaarCascade(
                                haar, 1.4, 4,
                                HAAR_DETECTION_TYPE.DO_CANNY_PRUNING | HAAR_DETECTION_TYPE.FIND_BIGGEST_OBJECT,
                                new Size(nextFrame.Width / 8, nextFrame.Height / 8)
                                )[0];

                if (markFaces)
                {
                    foreach (MCvAvgComp face in faces)
                    {
                        nextFrame.Draw(face.rect, new Bgr(0, double.MaxValue, 0), 3);
                    }
                }
            }

            return faces;
        }


        public String Recognize()
        {
            String found = "";
            MCvAvgComp[] faces;

            if (capture == null)
            {
                Capture();
            }

            faces = DetectFaces(false);
            if (faces.Length > 0)
            {
                // Found a face in the image
                Console.WriteLine("Found a face");

                // Train the recognizer if it hasn't been done (this takes a long time
                // so hopefully it's already been completed.
                if (trainingInitialized == false)
                {
                    TrainRecognizer();
                }

                // Get the image
                Image<Gray, Byte> testImage = new Image<Gray, Byte>(nextFrame.ToBitmap());

                // TODO: Crop the image to the face.

                // Pass that face to the EigenObjectRecognizer
                found = Recognizer.Recognize(testImage);
            }
            else
            {
                Console.WriteLine("Capture does not contain a face");
            }

            return found;
        }

        public void CaptureTrainingImages(String name, int wanted)
        {
            
            int success = 1;

            //String path = Properties.Settings.Default.DataDirectory + "\\Images\\Training\\" + name;
            string path = "";
            Directory.CreateDirectory(path);

            while (success <= wanted)
            {
                Console.WriteLine("Capturing {0}", success);
                Capture();

                MCvAvgComp[] faces = DetectFaces(false);
                if (faces.Length > 0)
                {
                    // TODO: All training images need to be the same orientation and size
                    // So I need to crop and transform images her to make sure that is so.
                    // Additionally, the image needs to be converted to greyscale (I think) and equalized
                    Save(path + "\\train" + success + ".bmp");
                    success++;
                    Console.WriteLine("Found face");
                }
                else
                {
                    Console.WriteLine("Bad image, retaking");
                }

                System.Threading.Thread.Sleep(1000);
            }

            Console.WriteLine("Done capturing training images");
        }

        public void TrainRecognizer()
        {

            //trainingImages = new Image<Gray, Byte>[60];


            //trainingImages[0] = new Image<Gray, byte>(Properties.Settings.Default.DataDirectory + "\\Images\\Training\\dan\\" + "train1.bmp");
            //trainingImages[1] = new Image<Gray, byte>(Properties.Settings.Default.DataDirectory + "\\Images\\Training\\dan\\" + "train2.bmp");
            //trainingImages[2] = new Image<Gray, byte>(Properties.Settings.Default.DataDirectory + "\\Images\\Training\\dan\\" + "train3.bmp");
            //trainingImages[3] = new Image<Gray, byte>(Properties.Settings.Default.DataDirectory + "\\Images\\Training\\dan\\" + "train4.bmp");
            //trainingImages[4] = new Image<Gray, byte>(Properties.Settings.Default.DataDirectory + "\\Images\\Training\\dan\\" + "train5.bmp");
            //trainingImages[5] = new Image<Gray, byte>(Properties.Settings.Default.DataDirectory + "\\Images\\Training\\dan\\" + "train6.bmp");
            //trainingImages[6] = new Image<Gray, byte>(Properties.Settings.Default.DataDirectory + "\\Images\\Training\\dan\\" + "train7.bmp");
            //trainingImages[7] = new Image<Gray, byte>(Properties.Settings.Default.DataDirectory + "\\Images\\Training\\dan\\" + "train8.bmp");
            //trainingImages[8] = new Image<Gray, byte>(Properties.Settings.Default.DataDirectory + "\\Images\\Training\\dan\\" + "train9.bmp");
            //trainingImages[9] = new Image<Gray, byte>(Properties.Settings.Default.DataDirectory + "\\Images\\Training\\dan\\" + "train10.bmp");
            //trainingImages[10] = new Image<Gray, byte>(Properties.Settings.Default.DataDirectory + "\\Images\\Training\\dan\\" + "train11.bmp");
            //trainingImages[11] = new Image<Gray, byte>(Properties.Settings.Default.DataDirectory + "\\Images\\Training\\dan\\" + "train12.bmp");
            //trainingImages[12] = new Image<Gray, byte>(Properties.Settings.Default.DataDirectory + "\\Images\\Training\\dan\\" + "train13.bmp");
            //trainingImages[13] = new Image<Gray, byte>(Properties.Settings.Default.DataDirectory + "\\Images\\Training\\dan\\" + "train14.bmp");
            //trainingImages[14] = new Image<Gray, byte>(Properties.Settings.Default.DataDirectory + "\\Images\\Training\\dan\\" + "train15.bmp");
            //trainingImages[15] = new Image<Gray, byte>(Properties.Settings.Default.DataDirectory + "\\Images\\Training\\dan\\" + "train16.bmp");
            //trainingImages[16] = new Image<Gray, byte>(Properties.Settings.Default.DataDirectory + "\\Images\\Training\\dan\\" + "train17.bmp");
            //trainingImages[17] = new Image<Gray, byte>(Properties.Settings.Default.DataDirectory + "\\Images\\Training\\dan\\" + "train18.bmp");
            //trainingImages[18] = new Image<Gray, byte>(Properties.Settings.Default.DataDirectory + "\\Images\\Training\\dan\\" + "train19.bmp");
            //trainingImages[19] = new Image<Gray, byte>(Properties.Settings.Default.DataDirectory + "\\Images\\Training\\dan\\" + "train20.bmp");
            //trainingImages[20] = new Image<Gray, byte>(Properties.Settings.Default.DataDirectory + "\\Images\\Training\\dan\\" + "train21.bmp");
            //trainingImages[21] = new Image<Gray, byte>(Properties.Settings.Default.DataDirectory + "\\Images\\Training\\dan\\" + "train22.bmp");
            //trainingImages[22] = new Image<Gray, byte>(Properties.Settings.Default.DataDirectory + "\\Images\\Training\\dan\\" + "train23.bmp");
            //trainingImages[23] = new Image<Gray, byte>(Properties.Settings.Default.DataDirectory + "\\Images\\Training\\dan\\" + "train24.bmp");
            //trainingImages[24] = new Image<Gray, byte>(Properties.Settings.Default.DataDirectory + "\\Images\\Training\\dan\\" + "train25.bmp");
            //trainingImages[25] = new Image<Gray, byte>(Properties.Settings.Default.DataDirectory + "\\Images\\Training\\dan\\" + "train26.bmp");
            //trainingImages[26] = new Image<Gray, byte>(Properties.Settings.Default.DataDirectory + "\\Images\\Training\\dan\\" + "train27.bmp");
            //trainingImages[27] = new Image<Gray, byte>(Properties.Settings.Default.DataDirectory + "\\Images\\Training\\dan\\" + "train28.bmp");
            //trainingImages[28] = new Image<Gray, byte>(Properties.Settings.Default.DataDirectory + "\\Images\\Training\\dan\\" + "train29.bmp");
            //trainingImages[29] = new Image<Gray, byte>(Properties.Settings.Default.DataDirectory + "\\Images\\Training\\dan\\" + "train30.bmp");

            //trainingImages[30] = new Image<Gray, byte>(Properties.Settings.Default.DataDirectory + "\\Images\\Training\\trina\\" + "train1.bmp");
            //trainingImages[31] = new Image<Gray, byte>(Properties.Settings.Default.DataDirectory + "\\Images\\Training\\trina\\" + "train2.bmp");
            //trainingImages[32] = new Image<Gray, byte>(Properties.Settings.Default.DataDirectory + "\\Images\\Training\\trina\\" + "train3.bmp");
            //trainingImages[33] = new Image<Gray, byte>(Properties.Settings.Default.DataDirectory + "\\Images\\Training\\trina\\" + "train4.bmp");
            //trainingImages[34] = new Image<Gray, byte>(Properties.Settings.Default.DataDirectory + "\\Images\\Training\\trina\\" + "train5.bmp");
            //trainingImages[35] = new Image<Gray, byte>(Properties.Settings.Default.DataDirectory + "\\Images\\Training\\trina\\" + "train6.bmp");
            //trainingImages[36] = new Image<Gray, byte>(Properties.Settings.Default.DataDirectory + "\\Images\\Training\\trina\\" + "train7.bmp");
            //trainingImages[37] = new Image<Gray, byte>(Properties.Settings.Default.DataDirectory + "\\Images\\Training\\trina\\" + "train8.bmp");
            //trainingImages[38] = new Image<Gray, byte>(Properties.Settings.Default.DataDirectory + "\\Images\\Training\\trina\\" + "train9.bmp");
            //trainingImages[39] = new Image<Gray, byte>(Properties.Settings.Default.DataDirectory + "\\Images\\Training\\trina\\" + "train10.bmp");
            //trainingImages[40] = new Image<Gray, byte>(Properties.Settings.Default.DataDirectory + "\\Images\\Training\\trina\\" + "train11.bmp");
            //trainingImages[41] = new Image<Gray, byte>(Properties.Settings.Default.DataDirectory + "\\Images\\Training\\trina\\" + "train12.bmp");
            //trainingImages[42] = new Image<Gray, byte>(Properties.Settings.Default.DataDirectory + "\\Images\\Training\\trina\\" + "train13.bmp");
            //trainingImages[43] = new Image<Gray, byte>(Properties.Settings.Default.DataDirectory + "\\Images\\Training\\trina\\" + "train14.bmp");
            //trainingImages[44] = new Image<Gray, byte>(Properties.Settings.Default.DataDirectory + "\\Images\\Training\\trina\\" + "train15.bmp");
            //trainingImages[45] = new Image<Gray, byte>(Properties.Settings.Default.DataDirectory + "\\Images\\Training\\trina\\" + "train16.bmp");
            //trainingImages[46] = new Image<Gray, byte>(Properties.Settings.Default.DataDirectory + "\\Images\\Training\\trina\\" + "train17.bmp");
            //trainingImages[47] = new Image<Gray, byte>(Properties.Settings.Default.DataDirectory + "\\Images\\Training\\trina\\" + "train18.bmp");
            //trainingImages[48] = new Image<Gray, byte>(Properties.Settings.Default.DataDirectory + "\\Images\\Training\\trina\\" + "train19.bmp");
            //trainingImages[49] = new Image<Gray, byte>(Properties.Settings.Default.DataDirectory + "\\Images\\Training\\trina\\" + "train20.bmp");
            //trainingImages[50] = new Image<Gray, byte>(Properties.Settings.Default.DataDirectory + "\\Images\\Training\\trina\\" + "train21.bmp");
            //trainingImages[51] = new Image<Gray, byte>(Properties.Settings.Default.DataDirectory + "\\Images\\Training\\trina\\" + "train22.bmp");
            //trainingImages[52] = new Image<Gray, byte>(Properties.Settings.Default.DataDirectory + "\\Images\\Training\\trina\\" + "train23.bmp");
            //trainingImages[53] = new Image<Gray, byte>(Properties.Settings.Default.DataDirectory + "\\Images\\Training\\trina\\" + "train24.bmp");
            //trainingImages[54] = new Image<Gray, byte>(Properties.Settings.Default.DataDirectory + "\\Images\\Training\\trina\\" + "train25.bmp");
            //trainingImages[55] = new Image<Gray, byte>(Properties.Settings.Default.DataDirectory + "\\Images\\Training\\trina\\" + "train26.bmp");
            //trainingImages[56] = new Image<Gray, byte>(Properties.Settings.Default.DataDirectory + "\\Images\\Training\\trina\\" + "train27.bmp");
            //trainingImages[57] = new Image<Gray, byte>(Properties.Settings.Default.DataDirectory + "\\Images\\Training\\trina\\" + "train28.bmp");
            //trainingImages[58] = new Image<Gray, byte>(Properties.Settings.Default.DataDirectory + "\\Images\\Training\\trina\\" + "train29.bmp");
            //trainingImages[59] = new Image<Gray, byte>(Properties.Settings.Default.DataDirectory + "\\Images\\Training\\trina\\" + "train30.bmp");

            //trainingLabels = new String[] { "Dan", "Dan", "Dan", "Dan", "Dan", "Dan", "Dan", "Dan", "Dan", "Dan", "Dan", "Dan", "Dan", "Dan", "Dan","Dan", "Dan", "Dan", "Dan", "Dan","Dan", "Dan", "Dan", "Dan", "Dan","Dan", "Dan", "Dan", "Dan", "Dan",
            //                                "Trina", "Trina", "Trina", "Trina", "Trina", "Trina", "Trina", "Trina", "Trina", "Trina","Trina", "Trina", "Trina", "Trina", "Trina","Trina", "Trina", "Trina", "Trina", "Trina","Trina", "Trina", "Trina", "Trina", "Trina","Trina", "Trina", "Trina", "Trina", "Trina"};

            //termCrit = new MCvTermCriteria(16, 0.001);

            //Recognizer = new EigenObjectRecognizer(trainingImages, trainingLabels, 15000, ref termCrit);

            trainingInitialized = true;
        }

        


    }

}
